//Author: bishisimo
//Time: 2021/7/8 下午8:07
//Describe: zerolog日志解析
package parser

import (
	"bytes"
	"gitee.com/bishisimo/log-parser/model"
	"github.com/rs/zerolog"
	"path"
	"runtime"
	"strings"
	"time"

	"encoding/json"
)

var DefaultZeroLogParser = new(ZeroLogParser)

type ZeroLogParser struct {
	ProjectName string
}

func NewZeroLogParser(projectName string) *ZeroLogParser {
	return &ZeroLogParser{
		ProjectName: projectName,
	}
}
func (p *ZeroLogParser) ParseLog(raw []byte) (*model.LogInfo, error) {
	logMap := make(map[string]interface{})
	d := json.NewDecoder(bytes.NewReader(raw))
	d.UseNumber()
	err := d.Decode(&logMap)
	if err != nil {
		return nil, err
	}
	li := &model.LogInfo{
		Lever:      p.ParseLogLever(logMap),
		Timestamp:  p.ParseTimestamp(logMap),
		CallerName: p.ParseCallerName(logMap),
		Location:   p.ParseLocation(logMap),
		Fields:     p.ParseFields(logMap),
		Msg:        p.ParseMsg(logMap),
	}
	return li, nil
}
func getCallerName() string {
	pc, _, _, _ := runtime.Caller(8)
	return runtime.FuncForPC(pc).Name()
}
func (p *ZeroLogParser) ParseLogLever(logMap map[string]interface{}) string {
	return logMap[zerolog.LevelFieldName].(string)
}
func (p *ZeroLogParser) ParseTimestamp(logMap map[string]interface{}) int64 {
	tr := logMap[zerolog.TimestampFieldName]
	switch t := tr.(type) {
	case string:
		td, err := time.Parse(time.RFC3339, t)
		if err != nil {
			return 0
		}
		return td.Unix()
	case int64:
		return t
	default:
		return 0
	}
}
func (p *ZeroLogParser) ParseCallerName(logMap map[string]interface{}) string {
	return path.Base(getCallerName())
}
func (p *ZeroLogParser) ParseLocation(logMap map[string]interface{}) string {
	d := logMap[zerolog.CallerFieldName]
	if d == nil {
		return "nil"
	}
	location := d.(string)
	if p.ProjectName == "" {
		location = path.Base(location)
	} else {
		sp := strings.Split(location, p.ProjectName)
		if len(sp) > 1 {
			location = sp[1][1:]
		}
	}
	return location
}

func (p *ZeroLogParser) ParseFields(logMap map[string]interface{}) map[string]interface{} {
	fields := make(map[string]interface{})
	for k, v := range logMap {
		switch k {
		case zerolog.LevelFieldName, zerolog.TimestampFieldName, zerolog.CallerFieldName, zerolog.MessageFieldName:
			continue
		default:
			fields[k] = v
		}
	}
	return fields
}

func (p *ZeroLogParser) ParseMsg(logMap map[string]interface{}) string {
	d := logMap[zerolog.MessageFieldName]
	if d == nil {
		return "nil"
	}
	return d.(string)
}
